// This file is part of Arduino ST4.
//
// Arduino ST4 is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Arduino ST4 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Arduino ST4.  If not, see <http://www.gnu.org/licenses/>.

// ASCOM Telescope driver for ArduinoST4
//
// Description:	Driver for an USB-ST4 interface implemented with an arduino.
//              Pulse guiding and slew is supported, allowing for very slow goto :)
//
// Implements:	ASCOM Telescope interface version: <To be completed by driver developer>
// Author:		(Kevin FERRARE) kevinferrare@gmail.com

using ASCOM.DeviceInterface;
using ASCOM.Utilities;
using System;
using System.Collections;
using System.Globalization;
using System.Runtime.InteropServices;

namespace ASCOM.ArduinoST4
{
    /// <summary>
    /// ASCOM Telescope Driver for ArduinoST4.
    /// A big part of the code in this class is autogenerated boilerplate for ASCOM API.
    /// Real logic is under AxisPositionController.
    /// 
    /// GUID is for COM identification of the component.
    /// ClassInterface is to make it invisible to other COM clients.
    /// </summary>
    /// Author:  Kevin Ferrare
    [Guid("045b2ced-6f70-4a3a-8483-1891f235deb1")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Telescope : ITelescopeV3, IDisposable
    {
        /// <summary>
        /// ASCOM DeviceID (COM ProgID) for this driver.
        /// The DeviceID is used by ASCOM applications to load the driver at runtime.
        /// </summary>
        internal static string driverID = "ASCOM.ArduinoST4.Telescope";

        /// <summary>
        /// Driver description that displays in the ASCOM Chooser.
        /// </summary>
        private static string driverDescription = "ArduinoST4 telescope driver";

        /// <summary>
        /// Right ascension and declination speed of the device in sideral multiple (earth rotation multiple)
        /// </summary>
        internal static double rightAscensionSideralRatePlus = 9;//8X Sideral Rate + 1 for earth rotation
        internal static double rightAscensionSideralRateMinus = 7;//8X Sideral Rate - 1 for earth rotation
        internal static double declinationSideralRatePlus = 8;
        internal static double declinationSideralRateMinus = 8;

        /// <summary>
        /// COM port for arduino access
        /// </summary>
        internal static string comPort = "COM4";

        /// <summary>
        /// Enable logging
        /// </summary>
        internal static bool traceState = true;

        /// <summary>
        /// ASCOM Utilities object, used only to calculate dates
        /// </summary>
        private Util utilities = new Util();

        /// <summary>
        /// Logger
        /// </summary>
        private TraceLogger traceLogger;

        /// <summary>
        /// Handles the communication with the arduino
        /// </summary>
        private DeviceController deviceController;

        /// <summary>
        /// Target position
        /// </summary>
        private double? targetRightAscension;
        private double? targetDeclination;

        /// <summary>
        /// Controller for each axis, talks with the device controller to start / stop movement, keeps track of the position while doing so
        /// </summary>
        private AxisController[] axisControllers;

        public Telescope()
        {
            // Read device configuration from the ASCOM Profile store
            ReadProfile();

            traceLogger = new TraceLogger("", "ArduinoST4");
            traceLogger.Enabled = traceState;
            traceLogger.LogMessage("Telescope", "Starting initialisation");

            deviceController = new DeviceController();

            //Setup the axes
            axisControllers = new AxisController[2];
            axisControllers[(int)Axis.RA] = new AxisController(Axis.RA, this.deviceController, -Constants.RA_PER_SECOND * rightAscensionSideralRateMinus, Constants.RA_PER_SECOND * rightAscensionSideralRatePlus);
            axisControllers[(int)Axis.DEC] = new AxisController(Axis.DEC, this.deviceController, -Constants.DEGREES_PER_SECOND * declinationSideralRateMinus, Constants.DEGREES_PER_SECOND * declinationSideralRatePlus);
            traceLogger.LogMessage("Telescope", "Completed initialisation");
        }


        #region Common properties and methods.

        /// <summary>
        /// Displays the Setup Dialog form.
        /// If the user clicks the OK button to dismiss the form, then
        /// the new settings are saved, otherwise the old values are reloaded.
        /// THIS IS THE ONLY PLACE WHERE SHOWING USER INTERFACE IS ALLOWED!
        /// </summary>
        public void SetupDialog()
        {
            // Only show the setup dialog if not connected
            if (this.deviceController.Connected)
            {
                System.Windows.Forms.MessageBox.Show("Already connected, just press OK");
                return;
            }

            using (SetupDialogForm setupDialogForm = new SetupDialogForm())
            {
                var result = setupDialogForm.ShowDialog();
                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    WriteProfile(); // Persist device configuration values to the ASCOM Profile store
                }
            }
        }

        public void CommandBlind(string command, bool raw)
        {
            CheckConnected("CommandBlind");
            // Call CommandString and return as soon as it finishes
            this.CommandString(command, raw);
        }

        public bool CommandBool(string command, bool raw)
        {
            CheckConnected("CommandBool");
            return deviceController.CommandBool(command);
        }

        public string CommandString(string command, bool raw)
        {
            CheckConnected("CommandString");
            return deviceController.CommandString(command);
        }

        public bool Connected
        {
            get
            {
                traceLogger.LogMessage("Connected Get", this.deviceController.Connected.ToString());
                return this.deviceController.Connected;
            }
            set
            {
                traceLogger.LogMessage("Connected Set", value.ToString());
                if (value == this.deviceController.Connected)
                {
                    return;
                }
                if (value)
                {
                    this.deviceController.Connect(comPort);
                }
                else
                {
                    this.deviceController.Disconnect();
                }
            }
        }

        public string Description
        {
            get
            {
                traceLogger.LogMessage("Description Get", driverDescription);
                return driverDescription;
            }
        }

        public string DriverInfo
        {
            get
            {
                Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
                string driverInfo = "Information about the driver itself. Version: " + DriverVersion;
                traceLogger.LogMessage("DriverInfo Get", driverInfo);
                return driverInfo;
            }
        }

        public string DriverVersion
        {
            get
            {
                Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
                string driverVersion = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor);
                traceLogger.LogMessage("DriverVersion Get", driverVersion);
                return driverVersion;
            }
        }

        public short InterfaceVersion
        {
            // set by the driver wizard
            get
            {
                traceLogger.LogMessage("InterfaceVersion Get", "3");
                return 3;
            }
        }

        public string Name
        {
            get
            {
                string name = "ArduinoST4";
                traceLogger.LogMessage("Name Get", name);
                return name;
            }
        }

        #endregion

        #region ITelescope Implementation
        public void AbortSlew()
        {
            axisControllers[(int)Axis.RA].Stop();
            axisControllers[(int)Axis.DEC].Stop();
        }

        public AlignmentModes AlignmentMode
        {
            get
            {
                return AlignmentModes.algGermanPolar;
            }
        }

        public IAxisRates AxisRates(TelescopeAxes axis)
        {
            traceLogger.LogMessage("AxisRates", "Get - " + axis.ToString());
            return new AxisRates(axis);
        }

        #region Capabilities
        public bool CanMoveAxis(TelescopeAxes axis)
        {
            traceLogger.LogMessage("CanMoveAxis", "Get - " + axis.ToString());
            switch (axis)
            {
                case TelescopeAxes.axisPrimary: return true;
                case TelescopeAxes.axisSecondary: return true;
                case TelescopeAxes.axisTertiary: return false;
                default: throw new InvalidValueException("CanMoveAxis", axis.ToString(), "0 to 2");
            }
        }

        public bool CanPulseGuide
        {
            get
            {
                traceLogger.LogMessage("CanPulseGuide", "Get - " + true.ToString());
                return true;
            }
        }

        public bool CanSetGuideRates
        {
            get
            {
                traceLogger.LogMessage("CanSetGuideRates", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSlew
        {
            get
            {
                traceLogger.LogMessage("CanSlew", "Get - " + true.ToString());
                return true;
            }
        }

        public bool CanSlewAsync
        {
            get
            {
                traceLogger.LogMessage("CanSlewAsync", "Get - " + true.ToString());
                return true;
            }
        }

        public bool CanSync
        {
            get
            {
                traceLogger.LogMessage("CanSync", "Get - " + true.ToString());
                return true;
            }
        }

        public bool CanFindHome
        {
            get
            {
                //No feedback on position=> cannot find home
                traceLogger.LogMessage("CanFindHome", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanPark
        {
            get
            {
                //No feedback on position=> cannot park
                traceLogger.LogMessage("CanPark", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSetDeclinationRate
        {
            get
            {
                traceLogger.LogMessage("CanSetDeclinationRate", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSetPark
        {
            get
            {
                traceLogger.LogMessage("CanSetPark", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSetPierSide
        {
            get
            {
                traceLogger.LogMessage("CanSetPierSide", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSetRightAscensionRate
        {
            get
            {
                traceLogger.LogMessage("CanSetRightAscensionRate", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSetTracking
        {
            get
            {
                traceLogger.LogMessage("CanSetTracking", "Get - " + false.ToString());
                return false;
            }
        }


        public bool CanSlewAltAz
        {
            get
            {
                traceLogger.LogMessage("CanSlewAltAz", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSlewAltAzAsync
        {
            get
            {
                traceLogger.LogMessage("CanSlewAltAzAsync", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanSyncAltAz
        {
            get
            {
                traceLogger.LogMessage("CanSyncAltAz", "Get - " + false.ToString());
                return false;
            }
        }

        public bool CanUnpark
        {
            get
            {
                traceLogger.LogMessage("CanUnpark", "Get - " + false.ToString());
                return false;
            }
        }

        #endregion


        public double Declination
        {
            get
            {
                return axisControllers[(int)Axis.DEC].Position;
            }
        }

        public EquatorialCoordinateType EquatorialSystem
        {
            get
            {
                EquatorialCoordinateType equatorialSystem = EquatorialCoordinateType.equLocalTopocentric;
                traceLogger.LogMessage("DeclinationRate", "Get - " + equatorialSystem.ToString());
                return equatorialSystem;
            }
        }

        public double GuideRateDeclination
        {
            get
            {
                return axisControllers[(int)Axis.DEC].SlewRate;
            }
            set
            {
                traceLogger.LogMessage("GuideRateDeclination Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("GuideRateDeclination", true);
            }
        }

        public double GuideRateRightAscension
        {
            get
            {
                return axisControllers[(int)Axis.RA].SlewRate;
            }
            set
            {
                traceLogger.LogMessage("GuideRateRightAscension Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("GuideRateRightAscension", true);
            }
        }

        public bool IsPulseGuiding
        {
            get
            {
                return this.Moving;
            }
        }

        public void MoveAxis(TelescopeAxes telescopeAxis, double rate)
        {
            traceLogger.LogMessage("MoveAxis", "TelescopeAxis - " + telescopeAxis.ToString() + "Rate - " + rate.ToString());
            //Some checks on given values for API conformity
            if (telescopeAxis == TelescopeAxes.axisTertiary)
            {
                throw new ASCOM.InvalidValueException("TelescopeAxes", telescopeAxis.ToString(), "No ternary axis on ST-4");
            }
            IRate axisRate = this.AxisRates(telescopeAxis)[1];
            if (Math.Abs(rate) > axisRate.Maximum || Math.Abs(rate) < axisRate.Minimum)
            {
                throw new ASCOM.InvalidValueException("AxisRate", rate.ToString(), axisRate.Minimum + ".." + axisRate.Maximum);
            }

            Orientation orientation = rate > 0 ? Orientation.PLUS : Orientation.MINUS;
            Axis axis = telescopeAxis == TelescopeAxes.axisPrimary ? Axis.RA : Axis.DEC;
            AxisController axisPositionController = axisControllers[(int)axis];
            if (rate == 0)
            {
                axisPositionController.Stop();
            }
            else
            {
                axisPositionController.Move(orientation);
            }
        }


        public void PulseGuide(GuideDirections direction, int duration)
        {
            traceLogger.LogMessage("PulseGuide", "Direction - " + direction.ToString() + "Duration - " + duration.ToString());
            //Duration in milliseconds
            TimeSpan moveDuration = TimeSpan.FromMilliseconds(duration);
            switch (direction)
            {
                case GuideDirections.guideEast:
                    axisControllers[(int)Axis.RA].Move(moveDuration, Orientation.PLUS);
                    break;
                case GuideDirections.guideWest:
                    axisControllers[(int)Axis.RA].Move(moveDuration, Orientation.MINUS);
                    break;
                case GuideDirections.guideNorth:
                    axisControllers[(int)Axis.DEC].Move(moveDuration, Orientation.PLUS);
                    break;
                case GuideDirections.guideSouth:
                    axisControllers[(int)Axis.DEC].Move(moveDuration, Orientation.MINUS);
                    break;
            }
        }

        public double RightAscension
        {
            get
            {
                double rightAscension = axisControllers[(int)Axis.RA].Position;
                rightAscension = (rightAscension % 24 + 24) % 24;//Ensure that RA is always positive and between 0 and 24
                return rightAscension;
            }
        }

        public double SiderealTime
        {
            get
            {
                double siderealTime = (18.697374558 + 24.065709824419081 * (utilities.DateLocalToJulian(DateTime.Now) - 2451545.0)) % 24.0;
                traceLogger.LogMessage("SiderealTime", "Get - " + siderealTime.ToString());
                return siderealTime;
            }
        }

        public void SlewToCoordinates(double rightAscension, double declination)
        {
            traceLogger.LogMessage("SlewToCoordinatesAsync", "RightAscension=" + rightAscension.ToString() + ", Declination=" + declination.ToString());
            SlewToCoordinatesAsync(rightAscension, declination);
            //Wait for both axes to finish
            this.axisControllers[(int)Axis.RA].WaitAsyncMoveEnd();
            this.axisControllers[(int)Axis.DEC].WaitAsyncMoveEnd();
        }

        public void SlewToCoordinatesAsync(double rightAscension, double declination)
        {
            traceLogger.LogMessage("SlewToCoordinatesAsync", "RightAscension=" + rightAscension.ToString() + ", Declination=" + declination.ToString() + " (current position ra=" + this.RightAscension + ", dec=" + this.Declination);
            double rightAscensionDelta = rightAscension - this.RightAscension;
            if (rightAscensionDelta < -12)
            {
                //Shortest path from 24 to 0
                rightAscensionDelta += 24;
            }
            else if (rightAscensionDelta > 12)
            {
                //Shortest path from 0 to 24
                rightAscensionDelta -= 24;
            }
            traceLogger.LogMessage("SlewToCoordinatesAsync", "RightAscension delta =" + rightAscensionDelta.ToString());
            double declinationDelta = declination - this.Declination;
            this.axisControllers[(int)Axis.RA].Move(rightAscensionDelta);
            this.axisControllers[(int)Axis.DEC].Move(declinationDelta);
        }

        public void SlewToTarget()
        {
            SlewToCoordinates(TargetRightAscension, TargetDeclination);
        }

        public void SlewToTargetAsync()
        {
            SlewToCoordinatesAsync(TargetRightAscension, TargetDeclination);
        }

        public bool Slewing
        {
            get
            {
                // Slewing and pulse guiding are the same for the hardware
                return IsPulseGuiding;
            }
        }

        public void SyncToCoordinates(double rightAscension, double declination)
        {
            axisControllers[(int)Axis.RA].Position = rightAscension;
            axisControllers[(int)Axis.DEC].Position = declination;
        }

        public void SyncToTarget()
        {
            SyncToCoordinates(TargetRightAscension, TargetDeclination);
        }

        public double TargetDeclination
        {
            get
            {
                if (this.targetDeclination == null)
                {
                    throw new ASCOM.ValueNotSetException("TargetDeclination");
                }
                return (double)this.targetDeclination;
            }
            set
            {
                if (value < -90 || value > 90)
                {
                    throw new ASCOM.InvalidValueException("TargetDeclination", value.ToString(), "-90..90");
                }
                this.targetDeclination = value;
            }
        }

        public double TargetRightAscension
        {
            get
            {
                if (this.targetRightAscension == null)
                {
                    throw new ASCOM.ValueNotSetException("TargetRightAscension");
                }
                return (double)this.targetRightAscension;
            }
            set
            {
                if (value < 0 || value > 24)
                {
                    throw new InvalidValueException("TargetRightAscension", value.ToString(), "0..24");
                }
                this.targetRightAscension = value;
            }
        }

        public bool Tracking
        {
            get
            {
                //EQ5 is always tracking when motors are on
                bool tracking = !this.Moving;
                traceLogger.LogMessage("Tracking Get", tracking.ToString());
                return tracking;
            }
            set
            {
                traceLogger.LogMessage("Tracking Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("Tracking", false);
            }
        }

        public DriveRates TrackingRate
        {
            get
            {
                return DriveRates.driveSidereal;
            }
            set
            {
            }
        }

        public ITrackingRates TrackingRates
        {
            get
            {
                traceLogger.LogMessage("TrackingRates", "Get - ");
                return new TrackingRates();
            }
        }

        public DateTime UTCDate
        {
            get
            {
                return DateTime.UtcNow;
            }
            set
            {
            }
        }

        #endregion

        #region Helpers

        #region ASCOM Registration

        // Register or unregister driver for ASCOM. This is harmless if already
        // registered or unregistered. 
        //
        /// <summary>
        /// Register or unregister the driver with the ASCOM Platform.
        /// This is harmless if the driver is already registered/unregistered.
        /// </summary>
        /// <param name="register">If <c>true</c>, registers the driver, otherwise unregisters it.</param>
        private static void RegUnregASCOM(bool register)
        {
            using (var profile = new ASCOM.Utilities.Profile())
            {
                profile.DeviceType = "Telescope";
                if (register)
                {
                    profile.Register(driverID, driverDescription);
                }
                else
                {
                    profile.Unregister(driverID);
                }
            }
        }

        /// <summary>
        /// This function registers the driver with the ASCOM Chooser and
        /// is called automatically whenever this class is registered for COM Interop.
        /// </summary>
        /// <param name="t">Type of the class being registered, not used.</param>
        /// <remarks>
        /// This method typically runs in two distinct situations:
        /// <list type="numbered">
        /// <item>
        /// In Visual Studio, when the project is successfully built.
        /// For this to work correctly, the option <c>Register for COM Interop</c>
        /// must be enabled in the project settings.
        /// </item>
        /// <item>During setup, when the installer registers the assembly for COM Interop.</item>
        /// </list>
        /// This technique should mean that it is never necessary to manually register a driver with ASCOM.
        /// </remarks>
        [ComRegisterFunction]
        public static void RegisterASCOM(Type t)
        {
            RegUnregASCOM(true);
        }

        /// <summary>
        /// This function unregisters the driver from the ASCOM Chooser and
        /// is called automatically whenever this class is unregistered from COM Interop.
        /// </summary>
        /// <param name="t">Type of the class being registered, not used.</param>
        /// <remarks>
        /// This method typically runs in two distinct situations:
        /// <list type="numbered">
        /// <item>
        /// In Visual Studio, when the project is cleaned or prior to rebuilding.
        /// For this to work correctly, the option <c>Register for COM Interop</c>
        /// must be enabled in the project settings.
        /// </item>
        /// <item>During uninstall, when the installer unregisters the assembly from COM Interop.</item>
        /// </list>
        /// This technique should mean that it is never necessary to manually unregister a driver from ASCOM.
        /// </remarks>
        [ComUnregisterFunction]
        public static void UnregisterASCOM(Type t)
        {
            RegUnregASCOM(false);
        }

        #endregion

        /// <summary>
        /// Throws an exception if not connected to the hardware
        /// </summary>
        /// <param name="message"></param>
        private void CheckConnected(string message)
        {
            if (!this.deviceController.Connected)
            {
                throw new ASCOM.NotConnectedException(message);
            }
        }

        /// <summary>
        /// Read the device configuration from the ASCOM Profile store
        /// </summary>
        internal void ReadProfile()
        {
            using (Profile driverProfile = new Profile())
            {
                driverProfile.DeviceType = "Telescope";
                traceState = ReadBoolFromProfile(driverProfile, "traceState", traceState);
                comPort = ReadStringFromProfile(driverProfile, "comPort", comPort);
                rightAscensionSideralRatePlus = ReadDoubleFromProfile(driverProfile, "rightAscensionSideralRatePlus", rightAscensionSideralRatePlus);
                rightAscensionSideralRateMinus = ReadDoubleFromProfile(driverProfile, "rightAscensionSideralRateMinus", rightAscensionSideralRateMinus);
                declinationSideralRatePlus = ReadDoubleFromProfile(driverProfile, "declinationSideralRatePlus", declinationSideralRatePlus);
                declinationSideralRateMinus = ReadDoubleFromProfile(driverProfile, "declinationSideralRateMinus", declinationSideralRateMinus);
            }
        }

        internal bool ReadBoolFromProfile(Profile driverProfile, String profileName, bool defaultValue)
        {
            return Convert.ToBoolean(ReadStringFromProfile(driverProfile, profileName, Convert.ToString(defaultValue)));
        }

        internal double ReadDoubleFromProfile(Profile driverProfile, String profileName, double defaultValue)
        {
            return Convert.ToDouble(ReadStringFromProfile(driverProfile, profileName, Convert.ToString(defaultValue)));
        }

        internal String ReadStringFromProfile(Profile driverProfile, String profileName, String defaultValue)
        {
            return driverProfile.GetValue(driverID, profileName, string.Empty, defaultValue);
        }

        /// <summary>
        /// Write the device configuration to the  ASCOM  Profile store
        /// </summary>
        internal void WriteProfile()
        {
            using (Profile driverProfile = new Profile())
            {
                driverProfile.DeviceType = "Telescope";
                driverProfile.WriteValue(driverID, "traceState", traceState.ToString());
                driverProfile.WriteValue(driverID, "comPort", comPort.ToString());
                driverProfile.WriteValue(driverID, "rightAscensionSideralRatePlus", rightAscensionSideralRatePlus.ToString());
                driverProfile.WriteValue(driverID, "rightAscensionSideralRateMinus", rightAscensionSideralRateMinus.ToString());
                driverProfile.WriteValue(driverID, "declinationSideralRatePlus", declinationSideralRatePlus.ToString());
                driverProfile.WriteValue(driverID, "declinationSideralRateMinus", declinationSideralRateMinus.ToString());
            }
        }

        internal Boolean Moving
        {
            get
            {
                return axisControllers[(int)Axis.RA].Moving || axisControllers[(int)Axis.DEC].Moving;
            }
        }
        #endregion

        #region unimplementable
        public ArrayList SupportedActions
        {
            get
            {
                traceLogger.LogMessage("SupportedActions Get", "Returning empty arraylist");
                return new ArrayList();
            }
        }

        public string Action(string actionName, string actionParameters)
        {
            throw new ASCOM.ActionNotImplementedException("Action " + actionName + " is not implemented by this driver");
        }

        public double Altitude
        {
            get
            {
                traceLogger.LogMessage("Altitude", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("Altitude", false);
            }
        }

        public double ApertureArea
        {
            get
            {
                traceLogger.LogMessage("ApertureArea Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("ApertureArea", false);
            }
        }

        public double ApertureDiameter
        {
            get
            {
                traceLogger.LogMessage("ApertureDiameter Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("ApertureDiameter", false);
            }
        }

        public bool AtHome
        {
            get
            {
                //No feedback on position=> never at home
                traceLogger.LogMessage("AtHome", "Get - " + false.ToString());
                return false;
            }
        }

        public bool AtPark
        {
            get
            {
                //No feedback on position=> never parked
                traceLogger.LogMessage("AtPark", "Get - " + false.ToString());
                return false;
            }
        }

        public double Azimuth
        {
            get
            {
                traceLogger.LogMessage("Azimuth Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("Azimuth", false);
            }
        }

        public double DeclinationRate
        {
            get
            {
                double declination = 0.0;
                traceLogger.LogMessage("DeclinationRate", "Get - " + declination.ToString());
                return declination;
            }
            set
            {
                traceLogger.LogMessage("DeclinationRate Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("DeclinationRate", true);
            }
        }

        public PierSide DestinationSideOfPier(double RightAscension, double Declination)
        {
            traceLogger.LogMessage("DestinationSideOfPier Get", "Not implemented");
            throw new ASCOM.PropertyNotImplementedException("DestinationSideOfPier", false);
        }

        public bool DoesRefraction
        {
            get
            {
                traceLogger.LogMessage("DoesRefraction Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("DoesRefraction", false);
            }
            set
            {
                traceLogger.LogMessage("DoesRefraction Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("DoesRefraction", true);
            }
        }

        public void FindHome()
        {
            traceLogger.LogMessage("FindHome", "Not implemented");
            throw new ASCOM.MethodNotImplementedException("FindHome");
        }

        public double FocalLength
        {
            get
            {
                traceLogger.LogMessage("FocalLength Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("FocalLength", false);
            }
        }


        public void Park()
        {
            traceLogger.LogMessage("Park", "Not implemented");
            throw new ASCOM.MethodNotImplementedException("Park");
        }

        public double RightAscensionRate
        {
            get
            {
                double rightAscensionRate = 0.0;
                traceLogger.LogMessage("RightAscensionRate", "Get - " + rightAscensionRate.ToString());
                return rightAscensionRate;
            }
            set
            {
                traceLogger.LogMessage("RightAscensionRate Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("RightAscensionRate", true);
            }
        }

        public void SetPark()
        {
            traceLogger.LogMessage("SetPark", "Not implemented");
            throw new ASCOM.MethodNotImplementedException("SetPark");
        }

        public PierSide SideOfPier
        {
            get
            {
                traceLogger.LogMessage("SideOfPier Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SideOfPier", false);
            }
            set
            {
                traceLogger.LogMessage("SideOfPier Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SideOfPier", true);
            }
        }

        public double SiteElevation
        {
            get
            {
                traceLogger.LogMessage("SiteElevation Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SiteElevation", false);
            }
            set
            {
                traceLogger.LogMessage("SiteElevation Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SiteElevation", true);
            }
        }

        public double SiteLatitude
        {
            get
            {
                traceLogger.LogMessage("SiteLatitude Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SiteLatitude", false);
            }
            set
            {
                traceLogger.LogMessage("SiteLatitude Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SiteLatitude", true);
            }
        }

        public double SiteLongitude
        {
            get
            {
                traceLogger.LogMessage("SiteLongitude Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SiteLongitude", false);
            }
            set
            {
                traceLogger.LogMessage("SiteLongitude Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SiteLongitude", true);
            }
        }

        public short SlewSettleTime
        {
            get
            {
                traceLogger.LogMessage("SlewSettleTime Get", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SlewSettleTime", false);
            }
            set
            {
                traceLogger.LogMessage("SlewSettleTime Set", "Not implemented");
                throw new ASCOM.PropertyNotImplementedException("SlewSettleTime", true);
            }
        }

        public void SlewToAltAz(double Azimuth, double Altitude)
        {
            traceLogger.LogMessage("SlewToAltAz", "Not implemented");
            throw new ASCOM.MethodNotImplementedException("SlewToAltAz");
        }

        public void SlewToAltAzAsync(double Azimuth, double Altitude)
        {
            traceLogger.LogMessage("SlewToAltAzAsync", "Not implemented");
            throw new ASCOM.MethodNotImplementedException("SlewToAltAzAsync");
        }

        public void SyncToAltAz(double Azimuth, double Altitude)
        {
            traceLogger.LogMessage("SyncToAltAz", "Not implemented");
            throw new ASCOM.MethodNotImplementedException("SyncToAltAz");
        }

        public void Unpark()
        {
            traceLogger.LogMessage("Unpark", "Not implemented");
            throw new ASCOM.MethodNotImplementedException("Unpark");
        }
        #endregion

        public void Dispose()
        {
            this.Connected = false;
            foreach (AxisController axisController in this.axisControllers)
            {
                axisController.Dispose();
            }
            this.deviceController.Dispose();
            // Clean up the tracelogger and util objects
            this.traceLogger.Enabled = false;
            this.traceLogger.Dispose();
            this.traceLogger = null;
            this.utilities.Dispose();
            this.utilities = null;
        }
    }
}
